package gt

import (
	"regexp"

	"gitee.com/dark.H/gs"
	"gitee.com/dark.H/gt/clip"
	termbox "github.com/nsf/termbox-go"
)

type TextPanel struct {
	loc         *PanelLoc
	cursorStart int
	cursorEnd   int
	numT        int
	pageNum     int
	pageContent gs.Str
	raw         gs.Str
	// rawMark         gs.Str
	cursorWord      gs.Str
	Label           gs.Str
	mode            int
	key             gs.Str
	foundWords      gs.Strs
	curosrfoundWord int
	After           func()
	mapKeys         map[rune]func(t *TextPanel)
}

func NewTextPanel(text gs.Str, loc ...*PanelLoc) *TextPanel {
	if loc != nil && loc[0] != nil {
		return &TextPanel{
			loc:     loc[0],
			raw:     text.Trim(),
			mapKeys: make(map[rune]func(t *TextPanel)),
		}
	} else {
		return &TextPanel{
			loc: &PanelLoc{
				X: 1,
				Y: 1,
			},
			raw:     text.Trim(),
			mapKeys: make(map[rune]func(t *TextPanel)),
		}
	}
}

func (tpanel *TextPanel) GetPageRow() int {
	r, _ := gs.GetWindowsSize()
	return r - 2
}
func (tpanel *TextPanel) PageNext() {
	tpanel.pageNum += 1
	r := tpanel.GetPageRow()
	if tpanel.pageNum*r >= tpanel.raw.Split("\n").Len() {
		tpanel.pageNum = 0
	}
}

func (tpanel *TextPanel) PageBefore() {
	tpanel.pageNum -= 1
	if tpanel.pageNum < 0 {
		tpanel.pageNum = 0
	}
}

func (tpanel *TextPanel) GetTextLine() int {
	return tpanel.raw[tpanel.cursorStart:].Count("\n")
}

func (tpanel *TextPanel) GetSelect() gs.Str {
	return tpanel.cursorWord
}

func (tp *TextPanel) OnKeyExtention(k rune, call func(t *TextPanel)) {
	tp.mapKeys[k] = call
}

func (tpanel *TextPanel) OnKey(k termbox.Event) bool {

	if tpanel.mode == 1 {
		// fmt.Println("", k, k.Ch)
		// time.Sleep(2 * time.Second)
		switch k.Key {
		case termbox.KeyEnter:
			tpanel.mode = 0

			tpanel.foundWords = tpanel.raw.Find(string(tpanel.key))
			if tpanel.foundWords.Len() > 0 {
				tpanel.curosrfoundWord = 0
			}
			tpanel.Label = gs.Str("Found keys: %d").F(tpanel.foundWords.Len())
			tpanel.key = gs.Str("")

			if tpanel.foundWords.Len() > 0 {
				startBuf := tpanel.raw[tpanel.cursorEnd:]
				e := tpanel.foundWords[tpanel.curosrfoundWord]
				offsetSt, offsetED := startBuf.Index(e)
				if offsetSt < 0 {
					tpanel.Label = gs.Str("Found Key: :%s | not found more!").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"))

				} else {
					tpanel.cursorStart = offsetSt + tpanel.cursorEnd
					tpanel.cursorEnd += offsetED
					tpanel.cursorWord = e
					tpanel.Label = gs.Str("Found Key: :%s | in %d:%d").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"), tpanel.cursorStart, tpanel.cursorEnd)

				}
				// tpanel.rawMark = tpanel.raw
				// tpanel.foundWords.Every(func(no int, i gs.Str) {
				// 	tpanel.rawMark = tpanel.rawMark.Replace(i.Str(), i.Color("U").Str())
				// })
			}

			// return false
		case termbox.KeyCtrlC:
			tpanel.mode = 0
			tpanel.key = gs.Str("")
			tpanel.foundWords = gs.Strs{}
			// return false
		case termbox.KeyTab:
			tpanel.key += gs.Str(`\S+`)
		case termbox.KeyDelete, 127:
			// fmt.Println("del ")
			// time.Sleep(2 * time.Second)
			if tpanel.key.Len() > 0 {
				tpanel.key = tpanel.key.Slice(0, -1)
			}
		case termbox.KeySpace:
			tpanel.key += gs.Str(" ")
		default:
			if gs.Str(k.Ch) != "" {
				tpanel.key += gs.Str(k.Ch)
			}
		}

		tpanel.Draw()
		return false

	} else if tpanel.mode == 2 {
		switch k.Key {
		case termbox.KeyEnter:
			text := tpanel.GetSelect()
			if text != "" {
				tpanel.drawWhiteScreen()

				user := Select([]string{
					"root",
					"other",
				})
				termbox.Init()
				if user == "other" {
					tpanel.drawWhiteScreen()
					user = InputLastLine().Str()
					termbox.Init()
				}
				pwd := Select(tpanel.GetSelectLine().Split(" ").Sort(gs.SortLen).List())
				host := gs.Str(user) + "@" + text
				if !host.In(":") {
					host += ":22"
				}
				jssh := gs.Dict[gs.Str]{
					"host": host,
					"pwd":  gs.Str(pwd),
				}
				if usePorxy := Select([]string{
					"no proxy",
					"socks5",
				}); usePorxy == "socks5" {
					tpanel.drawWhiteScreen()
					jssh["proxy"] = InputLastLine()
					termbox.Init()
				}
				jcopy := jssh.Json()
				clip.Write(jcopy)
				tpanel.Label = gs.Str("success pass to copy: use $0 -ssh | ") + gs.Str("%s").F(jcopy).Color("B", "r")
				termbox.Init()
				tpanel.mode = 0

				// } else {
				// 	tpanel.Label = gs.Str("failed connecting ") + gs.Str("%s [%s]").F(host, pwd).Color("B", "r")
				// }

				// termbox.Init()

			}

			tpanel.mode = 0
			// return false
		case termbox.KeyDelete, 127:
			// fmt.Println("del ")
			// time.Sleep(2 * time.Second)
			if tpanel.key.Len() > 0 {
				tpanel.key = tpanel.key.Slice(0, -1)
			}
		case termbox.KeySpace:
			tpanel.key += gs.Str(" ")
		default:
			if gs.Str(k.Ch) != "" {
				tpanel.key += gs.Str(k.Ch)
			}
		}
		tpanel.Draw()
		return false
	}

	switch k.Key {
	case termbox.KeyEnter:
		return true
	case termbox.KeyEsc:
		return true
	case termbox.KeyCtrlC:
		tpanel.foundWords = gs.Strs{}
		tpanel.Label = gs.Str("cancel found keys")
		// return false
	case termbox.KeyCtrlN:
		tpanel.PageNext()

	case termbox.KeyCtrlP:
		tpanel.PageBefore()
	}
	// fmt.Println(k, tpanel.cursorStart, tpanel.cursorEnd, tpanel.cursorWord.Color("g"))
	// time.Sleep(2 * time.Second)

	switch k.Ch {
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		if tpanel.numT == 0 {
			tpanel.numT = gs.Str(k.Ch).TryInt()
		} else {
			tpanel.numT = tpanel.numT*10 + gs.Str(k.Ch).TryInt()
		}
		tpanel.Label = gs.Str("set word num : %d").F(tpanel.numT).Color("B", "g")
	case '/':
		tpanel.Label = gs.Str("search mode:")
		// tpanel.rawMark = gs.Str("")
		tpanel.foundWords = gs.Strs{}
		tpanel.mode = 1
	case ':':
		tpanel.key = gs.Str("")
		tpanel.Label = gs.Str("command mode:")
		// tpanel.rawMark = gs.Str("")
		tpanel.mode = 2
	case 'i':
		tpanel.foundWords = tpanel.raw.Find(`\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`)
		if tpanel.foundWords.Len() > 0 {
			tpanel.curosrfoundWord = 0
		}
		tpanel.Label = gs.Str("Found keys: %d").F(tpanel.foundWords.Len())
		tpanel.key = gs.Str("")

		if tpanel.foundWords.Len() > 0 {
			startBuf := tpanel.raw[tpanel.cursorEnd:]
			e := tpanel.foundWords[tpanel.curosrfoundWord]
			offsetSt, offsetED := startBuf.Index(e)
			if offsetSt < 0 {
				tpanel.Label = gs.Str("Found Key: :%s | not found more!").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"))

			} else {
				tpanel.cursorStart = offsetSt + tpanel.cursorEnd
				tpanel.cursorEnd += offsetED
				tpanel.cursorWord = e
				tpanel.Label = gs.Str("Found Key: :%s | in %d:%d").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"), tpanel.cursorStart, tpanel.cursorEnd)

			}

		}

	case 'h':
		if tpanel.foundWords.Len() > 0 && tpanel.curosrfoundWord < tpanel.foundWords.Len() {

			tpanel.curosrfoundWord = (tpanel.curosrfoundWord - 1) % tpanel.foundWords.Len()
			if tpanel.curosrfoundWord < 0 {
				tpanel.curosrfoundWord = 0
			}
			e := tpanel.foundWords[tpanel.curosrfoundWord]
			tpanel.Label = gs.Str("Switch Key: ") + e.Color("B", "g")

			startBuf := tpanel.raw[:tpanel.cursorStart]

			cc, _ := startBuf.Rindex(e)
			if cc < 0 {
				tpanel.Label = gs.Str("Found Key: :%s | not found more!").F(e.Color("B", "g"))
			} else {
				tpanel.cursorStart = cc
				_, tpanel.cursorEnd = startBuf.Rindex(e)
				tpanel.cursorWord = e
				tpanel.Label = gs.Str("Found Key: :%s | in %d:%d").F(e.Color("B", "g"), tpanel.cursorStart, tpanel.cursorEnd)

			}

			// startBuf := tpanel.raw[tpanel.cursorEnd:]
			// e := tpanel.foundWords[tpanel.curosrfoundWord]
			// offsetSt, offsetED := startBuf.Index(e)
			// if offsetSt < 0 {
			// 	tpanel.Label = gs.Str("Found Key: :%s | not found more!").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"))

			// } else {
			// 	tpanel.cursorStart = offsetSt + tpanel.cursorEnd
			// 	tpanel.cursorEnd += offsetED
			// 	tpanel.cursorWord = e
			// 	tpanel.Label = gs.Str("Found Key: :%s | in %d:%d").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"), tpanel.cursorStart, tpanel.cursorEnd)

			// }
		}
	case 'l':
		if tpanel.foundWords.Len() > 0 {
			tpanel.curosrfoundWord = (tpanel.curosrfoundWord + 1) % tpanel.foundWords.Len()
			tpanel.Label = gs.Str("Switch Key: ") + tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g")

			startBuf := tpanel.raw[tpanel.cursorEnd:]
			e := tpanel.foundWords[tpanel.curosrfoundWord]
			offsetSt, offsetED := startBuf.Index(e)
			if offsetSt < 0 {
				// tpanel.Label = gs.Str("Found Key: :%s | not found more!").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"))
				startBuf := tpanel.raw[:tpanel.cursorStart]
				offsetSt, offsetED := startBuf.Index(e)
				tpanel.cursorStart = offsetSt
				tpanel.cursorEnd = offsetED
				tpanel.cursorWord = e
				tpanel.Label = gs.Str("Found Key: :%s | in %d:%d").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"), tpanel.cursorStart, tpanel.cursorEnd)
			} else {
				tpanel.cursorStart = offsetSt + tpanel.cursorEnd
				tpanel.cursorEnd += offsetED
				tpanel.cursorWord = e
				tpanel.Label = gs.Str("Found Key: :%s | in %d:%d").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"), tpanel.cursorStart, tpanel.cursorEnd)

			}
		}

	case 'n', 'j':

		if tpanel.foundWords.Len() > 0 {
			startBuf := tpanel.raw[tpanel.cursorEnd:]
			e := tpanel.foundWords[tpanel.curosrfoundWord]
			offsetSt, offsetED := startBuf.Index(e)
			if offsetSt < 0 {
				tpanel.Label = gs.Str("Found Key: :%s | not found more!").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"))

			} else {
				tpanel.cursorStart = offsetSt + tpanel.cursorEnd
				tpanel.cursorEnd += offsetED
				tpanel.cursorWord = e
				tpanel.Label = gs.Str("Found Key: :%s | in %d:%d").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"), tpanel.cursorStart, tpanel.cursorEnd)

			}

		} else {
			startBuf := tpanel.raw[tpanel.cursorEnd:]
			if tpanel.cursorEnd == tpanel.raw.Len()-1 {
				startBuf = tpanel.raw
				tpanel.cursorStart = 0
				tpanel.cursorEnd = 0
			}
			fs := startBuf.Find(`\S+`)
			if fs.Len() > 0 {
				if tpanel.numT == 0 || tpanel.numT == 1 {
					e := fs[0]
					offsetSt, offsetED := startBuf.Index(e)
					tpanel.cursorStart = offsetSt + tpanel.cursorEnd
					tpanel.cursorEnd += offsetED
					tpanel.cursorWord = e
				} else {

					for c := 0; c < tpanel.numT && c < fs.Len(); c++ {
						startBuf := tpanel.raw[tpanel.cursorEnd:]
						// fs := startBuf.Find(`\S+`)
						e := fs[c]

						offsetSt, offsetED := startBuf.Index(e)
						if c == 0 {
							tpanel.cursorStart = offsetSt + tpanel.cursorEnd
						}
						tpanel.cursorEnd += offsetED

					}
					tpanel.cursorWord = tpanel.raw[tpanel.cursorStart:tpanel.cursorEnd]
				}
				tpanel.numT = 0

			}
			tpanel.Label = gs.Str("cursor: %d: %d | len: %d").F(tpanel.cursorStart, tpanel.cursorEnd, tpanel.raw.Len()).Color("B", "g")

		}
	case 'p', 'k':
		if tpanel.foundWords.Len() == 0 {
			tpanel.numT = 0
			startBuf := tpanel.raw[:tpanel.cursorStart]
			fs := startBuf.Find(`\S+`)

			if fs.Len() > 0 {
				e := fs.Last()
				tpanel.cursorStart, tpanel.cursorEnd = startBuf.Rindex(e)

				tpanel.cursorWord = e
			}
			tpanel.Label = gs.Str("cursor : %d: %d").F(tpanel.cursorStart, tpanel.cursorEnd).Color("B", "g")

		} else {
			e := tpanel.foundWords[tpanel.curosrfoundWord]
			startBuf := tpanel.raw[:tpanel.cursorStart]
			c, _ := startBuf.Rindex(e)

			if c < 0 {
				tpanel.Label = gs.Str("Found Key: :%s | not found more!").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"))
			} else {
				tpanel.cursorStart, tpanel.cursorEnd = startBuf.Rindex(e)
				tpanel.cursorWord = e
				tpanel.Label = gs.Str("Found Key: :%s | in %d:%d").F(tpanel.foundWords[tpanel.curosrfoundWord].Color("B", "g"), tpanel.cursorStart, tpanel.cursorEnd)

			}
		}
	default:
		if call, ok := tpanel.mapKeys[k.Ch]; ok {
			call(tpanel)
		}
	}
	tpanel.Draw()
	return false
}

func (tp *TextPanel) Focus(byHit int) {
	row := tp.GetPageRow()
	tp.pageNum = (tp.raw[:byHit].Split("\n").Len() - 1) / row
}

func (tp *TextPanel) draw(t ...gs.Str) {

	row := tp.GetPageRow()
	cc := tp.raw.Trim()
	// if tp.rawMark != "" {
	// 	cc = tp.rawMark
	// }

	if tp.cursorWord != "" {
		tp.Focus(tp.cursorEnd)
	}

	if t != nil {
		cc = t[0].Trim()
	}

	lines := cc.Split("\n")
	if lines.Len() < row {
		cc.Print()
	} else {
		et := tp.pageNum*row + row
		cc2 := gs.Str("")
		if et >= lines.Len() {
			et = lines.Len()
			ee := gs.Str(" ").Repeat(tp.loc.maxWidth)
			cc2 = (ee + "\n").Repeat(row - (et % row))
		}

		cc := lines[tp.pageNum*row : et].Join("\n")

		(cc + cc2).Print()

	}

}

func (tp *TextPanel) GetSelectLineNum() int {
	return tp.raw[:tp.cursorStart].Count("\n")
}

func (tp *TextPanel) GetSelectLine() gs.Str {
	lines := tp.raw.Split("\n")
	no := tp.GetSelectLineNum()
	if no < lines.Len() {
		return lines[no]
	} else {
		return ""
	}
}

func (tp *TextPanel) drawWhiteScreen() {
	tp.loc.Cusor().Reset().Print(true)
	ee := gs.Str(" ").Repeat(tp.loc.maxWidth)
	row := tp.GetPageRow()
	cc2 := (ee + "\n").Repeat(row)
	cc2.Print()
}

func (tpanel *TextPanel) Draw() {
	tpanel.drawWhiteScreen()
	tpanel.loc.Cusor().Reset().Print(true)
	if tpanel.cursorStart < 0 {
		tpanel.cursorStart = 0
	}
	if tpanel.cursorEnd < 0 {
		tpanel.cursorEnd = 0
	}

	if tpanel.mode == 0 {
		cc := tpanel.raw

		pre := cc[:tpanel.cursorStart]
		hit := cc[tpanel.cursorStart:tpanel.cursorEnd]
		end := cc[tpanel.cursorEnd:]
		if tpanel.foundWords.Len() > 0 {
			tpanel.foundWords.Every(func(no int, i gs.Str) {
				pre = pre.Replace(i.Str(), i.Color("U", "b").Str())
				end = end.Replace(i.Str(), i.Color("U", "b").Str())
			})
		}
		tpanel.draw(pre + hit.Color("B", "U", "F", "g") + end)

		if tpanel.Label != "" {
			tpanel.loc.Label(tpanel.Label, -1)
		}

	} else if tpanel.mode == 1 {
		_, err := regexp.Compile(tpanel.key.Str())
		founds := gs.Strs{}
		if err != nil {
			// founds = tpanel.raw.Find(string(tpanel.key))
		} else {
			founds = tpanel.raw.Find(string(tpanel.key))

		}
		if founds.Len() == 0 {
			tpanel.key = gs.Str("")

			tpanel.draw()
			tpanel.Label = gs.Str("search mode:").Add(tpanel.key.Color("U"))
		} else {
			t := tpanel.raw
			for _, k := range founds {
				t = t.Replace(k.Str(), k.Color("b", "B", "U").Str())
			}
			tpanel.draw(t)
			tpanel.Label = gs.Str("search mode:").Add(tpanel.key.Color("U"))
		}

		if tpanel.Label != "" {
			tpanel.loc.Label(tpanel.Label+"                         ", -1)
		}
	} else if tpanel.mode == 2 {
		cc := tpanel.raw

		pre := cc[:tpanel.cursorStart]
		hit := cc[tpanel.cursorStart:tpanel.cursorEnd]
		end := cc[tpanel.cursorEnd:]
		if tpanel.foundWords.Len() > 0 {
			tpanel.foundWords.Every(func(no int, i gs.Str) {
				pre = pre.Replace(i.Str(), i.Color("U", "b").Str())
				end = end.Replace(i.Str(), i.Color("U", "b").Str())
			})
		}
		tpanel.draw(pre + hit.Color("B", "U", "F", "g") + end)

		tpanel.Label = gs.Str("cmd mode:").Add(tpanel.key.Color("U"))
		if tpanel.Label != "" {
			tpanel.loc.Label(tpanel.Label+"                           ", -1)
		}
	}
}

func ClearScreen() {

	row, width := gs.GetWindowsSize()
	w := gs.Str("").ANSISave().ANSICursor(1, 1).ANSIEraseALLType2()
	ee := gs.Str(" ").Repeat(width)
	cc2 := w + (ee + "\n").Repeat(row)
	cc2.Print()

}
